/*
    Copyright 2008, 2009, 2010 Brightworks, Inc.

    This file is part of Language Mentor.

    Language Mentor is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Language Mentor is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Language Mentor.  If not, see <http://www.gnu.org/licenses/>.
*/
package com.langcollab.languagementor.controller
{
    import com.brightworks.error.BwErrorEvent;
    import com.brightworks.util.Debug;
    import com.brightworks.util.Utils_Object;
    import com.brightworks.util.download.FileDownloader;
    import com.brightworks.util.download.FileSetDownloader;
    import com.langcollab.languagementor.constant.Constant_Misc;
    import com.langcollab.languagementor.model.MainModel;

    import flash.events.Event;
    import flash.system.Security;
    import flash.utils.ByteArray;
    import flash.utils.Dictionary;

    import org.swizframework.utils.commands.IEventAwareCommand;

    public class Command_DownloadRepositoryData extends Command_Base implements IEventAwareCommand
    {
        public static const STATUS_COMPLETE:String                           = "complete";
        public static const STATUS_DOWNLOADING_CONTENT_PROVIDER_XML:String   = "downloadingContentProviderXML";
        public static const STATUS_DOWNLOADING_MODULE_XML:String             = "downloadingContentProviderXML";

        [Inject]
        public var model:MainModel;
        public var status:String;

        private var _contentProviderFileDownloader:FileDownloader;
        private var _currentRepositoryXML:XML;
        private var _errorCount:int = 0;
        private var _moduleFileSetDownloader:FileSetDownloader;
        private var _modulesFolderUrl:String;

        // --------------------------------------------
        //
        //           Public Methods
        //
        // --------------------------------------------

        public function Command_DownloadRepositoryData()
        {
            super();
        }

        public function dispose():void {
            _contentProviderFileDownloader.dispose();
            _contentProviderFileDownloader = null;
            _moduleFileSetDownloader.dispose();
            _moduleFileSetDownloader = null;
            _currentRepositoryXML = null;
        }

        public function execute():void
        {
            _modulesFolderUrl = model.currentRepositoryRootURL;
            // Currently doing this in two places - here and DownloadProcessManager
            //Security.loadPolicyFile(_modulesFolderUrl + "/" + Constant_Misc.FILEPATHINFO_DOWNLOAD_PERMISSION_FILE_NAME);
            status = STATUS_DOWNLOADING_CONTENT_PROVIDER_XML;
            _contentProviderFileDownloader = new FileDownloader();
            _contentProviderFileDownloader.downloadFolderURL      = _modulesFolderUrl;
            _contentProviderFileDownloader.downloadFileName       = Constant_Misc.FILEPATHINFO_DOWNLOADS_CONTENT_PROVIDER_XML_FILE_NAME;
            _contentProviderFileDownloader.downloadFileExtension  = "xml";
            _contentProviderFileDownloader.addEventListener(Event.COMPLETE, onContentProviderFileDownloadComplete);
            _contentProviderFileDownloader.addEventListener(BwErrorEvent.ERROR_GENERIC, onContentProviderFileDownloadFailure);
            _contentProviderFileDownloader.start();
        }

        override public function fault(info:Object = null):void
        {
            //// implement user-friendly debug call
            Debug.error_Fatal(["Command_DownloadRepositoryData.fault():", info]);
        }

        // --------------------------------------------
        //
        //           Private Methods
        //
        // --------------------------------------------

        private function onContentProviderFileDownloadComplete(event:Event):void {
            status = STATUS_DOWNLOADING_MODULE_XML;
            var currentRepositoryXMLFileData:ByteArray = FileDownloader(event.target).fileData;
            _currentRepositoryXML = new XML(String(currentRepositoryXMLFileData));
            if (XMLList(_currentRepositoryXML.moduleIds).length() != 1) 
                Debug.error_Fatal(["Command_DownloadRepositoryData.onContentProviderFileDownloadComplete(): Content provider XML needs to have exactly one 'moduleId' element, and it has" + XMLList(_currentRepositoryXML.moduleIds).length(), _currentRepositoryXML.toXMLString()]);
            var fileDataForFileSetDownloader:Dictionary = new Dictionary();
            var moduleIdNodeList:XMLList = _currentRepositoryXML.moduleIds[0].moduleId;
            var moduleIdNode:XML;
            var moduleIdString:String;
            var fileDataForFileDownloader:Object;
            for each (moduleIdNode in moduleIdNodeList) {
                moduleIdString = moduleIdNode.toString();
                fileDataForFileDownloader = new Object();
                fileDataForFileDownloader["downloadFolderURL"]      = _modulesFolderUrl + "/" + moduleIdString;
                fileDataForFileDownloader["downloadFileName"]       = Constant_Misc.FILEPATHINFO_DOWNLOADS_MODULE_XML_FILE_NAME;
                fileDataForFileDownloader["downloadFileExtension"]  = "xml";
                fileDataForFileSetDownloader[moduleIdString]        = fileDataForFileDownloader;
            }
            _moduleFileSetDownloader = new FileSetDownloader();
            _moduleFileSetDownloader.fileData               = fileDataForFileSetDownloader;
            _moduleFileSetDownloader.downloadFileExtension  = "xml";
            _moduleFileSetDownloader.downloadURLRoot        = _modulesFolderUrl;
            _moduleFileSetDownloader.addEventListener(Event.COMPLETE, onModuleFilesDownloadComplete);
            _moduleFileSetDownloader.addEventListener(BwErrorEvent.ERROR_GENERIC, onModuleFilesDownloadFailure);
            _moduleFileSetDownloader.start();
        }

        private function onContentProviderFileDownloadFailure(event:BwErrorEvent):void {
            _errorCount++;
            if (_errorCount <= 10)
            {
                _contentProviderFileDownloader.start();
            }
            else
            {
                var message:String = "Command_DownloadRepositoryData.onContentProviderFileDownloadFailure()";
                var a:Array = Utils_Object.cloneInstance(event.errorInfoArray);
                a.unshift(message);
                //// implement user-friendly debug call
                Debug.error_Fatal(a);
            }
        }

        private function onModuleFilesDownloadComplete(event:Event):void {
            var result:XML = _currentRepositoryXML.copy();
            var modulesNode:XML = <modules/>;
            result.appendChild(modulesNode);
            var fileId:String;
            var oneFilesInfo:Object;
            var oneFilesByteData:ByteArray;
            var oneFilesXML:XML;
            var moduleIdNode:XML;
            var moduleIdNodeString:String;
            for (fileId in _moduleFileSetDownloader.fileData) {
                oneFilesInfo = _moduleFileSetDownloader.fileData[fileId];
                oneFilesByteData = oneFilesInfo.fileData;
                oneFilesXML = new XML(String(oneFilesByteData));
                moduleIdNodeString = "<publishedModuleVersionId>" + fileId + "</publishedModuleVersionId>";
                moduleIdNode = new XML(moduleIdNodeString);
                oneFilesXML.appendChild(moduleIdNode);
                modulesNode.appendChild(oneFilesXML);
            }
            validateXML(result);
            model.currentRepositoryXML = result;
            this.result();
            dispose();
        }

        private function onModuleFilesDownloadFailure(event:BwErrorEvent):void {
            var message:String = "Command_DownloadRepositoryData.onModuleFilesDownloadFailure()";
            var a:Array = Utils_Object.cloneInstance(event.errorInfoArray);
            a.unshift(message);
            //// implement user-friendly debug call
            Debug.error_Fatal(a);
        }

        private function validateXML(xml:XML):void {
            var bError:Boolean = false;
            var errorList:Array = [];
            // exactly one contentProviderId node
            if (XMLList(xml.contentProviderId).length() != 1) {
                bError = true;
                errorList.push("Content Provider XML should have one 'contentProviderId' node, but it has " + XMLList(xml.contentProviderId).length() + ".");
            }
            // contentProviderId string's length
            else if (xml.contentProviderId[0].toString().length < 15) {
                bError = true;
                errorList.push("Content Provider XML's 'contentProviderId' is only " + xml.contentProviderId[0].toString().length + " characters long. Are you kidding?! It's really important to provide an ID that is unique. If you don't do so, this could create problems - not only for yourself but also for many other people. Please be kind, and create an ID that meets the standards set forth in the Language Collaborative documentation.");
            }
            if (bError) 
                Debug.error_Fatal(["Your content provider XML file and/or one or more of your module XML files has a problem. Here are some details:", errorList]);
            /// There's a lot of stuff that we should do here...
            // both file types only have allowed node types?
            // contentProvider has exactly one moduleIds node
            // contentProvider moduleIds node has only moduleId nodes
            // contentProvider has 0 or 1 contentProviderDisplayName nodes
            // modules have exactly one nativeLanguageIso639_3Code node
            // modules have exactly one targetLanguageIso639_3Code node
            // modules have exactly one nativeLanguages node
            // modules have exactly one targetLanguages node
            //   each of the above two nodes has 1-n language nodes
            //   each language node has 1 iso639_3Code node
            //   each language node has 1 moduleName node
            //   each language node has 0-1 description node
            // modules have 0 or 1 level nodes
            // modules have 0 or 1 tags nodes
            // tags nodes only have tag nodes
            // tag nodes have exactly 1 text node
            // tag nodes have exactly 1 languageIso639_3Code node
        }
    }
}


